package de.koller.balance.render;

import de.koller.aframe.gl.AFrame;
import de.koller.aframe.gl.Utils;
import de.koller.aframe.util.filter.Combine;
import de.koller.aframe.util.filter.DownSample;
import de.koller.balance.BNode;
import de.koller.balance.render.filter.Glow;
import de.koller.balance.scene.CommonUses;
import de.koller.oceanFramework.gl.OceanGame;
import de.koller.oceanFramework.gl.util.ImagePreview;
import de.koller.oceanFramework.gl.util.ParticleTexture;
import de.koller.oceanFramework.tree.Child;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import org.lwjgl.opengl.GL11;

abstract public class BRenderExt extends BNode {

	private final boolean highAntialias = true;
	public final int aliasRes = highAntialias ? 6 : 1;
	private boolean glow = false;
	
	private boolean showPreview = true;

	@Child public final CommonUses commonUses = new CommonUses();
	
	public final int screenW = 1920 / 4, screenH = 1080 / 4;

	public AFrame frame = new AFrame( screenW*(highAntialias?aliasRes:1), screenH*(highAntialias?aliasRes:1) );
	
	private ParticleTexture currentBigTexture;
	private ParticleTexture currentSmallTexture;
	
	private ParticleTexture completeFrameTexture;
	private ParticleTexture completeLightTexture;
	
	private ParticleTexture completeTmp;
	private ParticleTexture out;
	
	public boolean renderLight = false;
	public int subX = -1;
	public int subY = -1;
	
	@Override
	protected void init() {
		super.init();

		frame.setUp();
		currentBigTexture = ParticleTexture.generate( screenW*(highAntialias?aliasRes:1), screenH*(highAntialias?aliasRes:1) );
		currentSmallTexture = ParticleTexture.generate( screenW, screenH );
		
		completeFrameTexture = ParticleTexture.generate( screenW*4, screenH*4 );
		completeLightTexture = ParticleTexture.generate( screenW*4, screenH*4 );
		
		completeTmp = ParticleTexture.generate( screenW*4, screenH*4 );
		out = ParticleTexture.generate( screenW*4, screenH*4 );
		
//		GL11.glEnable( GL11.GL_CULL_FACE );
//		GL11.glCullFace( GL11.GL_BACK );

		GL11.glEnable( GL11.GL_DEPTH_TEST );
		GL11.glDepthFunc( GL11.GL_LEQUAL );
		
		GL11.glAlphaFunc( GL11.GL_GREATER, 0.01f );
		GL11.glEnable( GL11.GL_ALPHA_TEST );
	}
	
	public void renderGame( int elapsedTime ) {		
		//GL11.glTexParameteri( GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S, GL11.GL_CLAMP );
		//GL11.glTexParameteri( GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_T, GL11.GL_CLAMP );
		GL11.glClearColor( 0, 0, 0, 1 );
		GL11.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT | GL11.GL_STENCIL_BUFFER_BIT);
		
		GL11.glHint( GL11.GL_PERSPECTIVE_CORRECTION_HINT, GL11.GL_NICEST );
		GL11.glHint( GL11.GL_LINE_SMOOTH_HINT, GL11.GL_NICEST );
		GL11.glHint( GL11.GL_POLYGON_SMOOTH_HINT, GL11.GL_NICEST );
		GL11.glHint( GL11.GL_POINT_SMOOTH_HINT, GL11.GL_NICEST );
		
		GL11.glEnable( GL11.GL_BLEND );
		GL11.glBlendFunc( GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA );
		
		if( !broot.quality ) {
			renderSzene( -1, -1, false );
			return;
		}
		
		for( int ix = 0; ix < 4; ix++ )
			for( int iy = 0; iy < 4; iy++ ) {
				System.out.print( "start render subFrame:" + ix + "/" + iy + " -> " );
				
				// --- Frame
				frame.bind();
				renderSzene( ix, iy, false );
				frame.unbind();
				
				frame.bindTexture();
				Utils.loadTextureData( currentBigTexture );
				antialias();
				loadIn( completeFrameTexture, currentSmallTexture, ix, iy );
				
				System.out.print( "frame completed, start light\n" );
				
				// --- Light
				frame.bind();
				renderSzene( ix, iy, true );
				frame.unbind();
				
				frame.bindTexture();
				Utils.loadTextureData( currentBigTexture );
				antialias();
				loadIn( completeLightTexture, currentSmallTexture, ix, iy );
			}
				
		if( showPreview ) {
			ImagePreview.showRaster( completeFrameTexture, screenW*2, screenH*2 );
			ImagePreview.showRaster( completeLightTexture, screenW*2, screenH*2 );
		}
		
		if( glow ) {
			Glow.useLin( completeLightTexture, completeTmp );
			//ImagePreview.showRaster( completeTmp, screenW*2, screenH*2 );
			Combine.max( completeFrameTexture, completeTmp, out );
		} else {
			System.arraycopy( completeFrameTexture.getData(), 0, out.getData(), 0, out.getData().length );
			out.flush();
		}
		
		renderFrame();
		
		OceanGame.checkForErrorGL( "GLError" );
	}

	private void antialias() {
		if( highAntialias )
			DownSample.useHigh( currentBigTexture, currentSmallTexture );
		else
			System.arraycopy( currentBigTexture.getData(), 0, currentSmallTexture.getData(), 0, currentBigTexture.getData().length );
			//DownSample.use( currentBigTexture, currentSmallTexture );
		currentSmallTexture.flush();
	}
	
	private void loadIn( ParticleTexture completeTexture, ParticleTexture part, int subX, int subY ) {
		for( int x = 0; x < part.getWidth(); x++ )
			for( int y = 0; y < part.getHeight(); y++ ) {
				completeTexture.setPixel( part.colorAt( x, y ), 
						subX * part.getWidth() + x,
						subY * part.getHeight() + y );
			}
		
		completeTexture.flush();
	}
	
	private void renderSzene( int sx, int sy, boolean light ) {
		renderLight = light;
		subX = sx; subY = sy;
		renderSzene();
	}
	
	/** if subX or subY == -1, the whole frame should be rendered */
	abstract public void renderSzene();

	private void renderFrame() {
		if( broot.quality ) {
			System.out.println( "fertig" );

			new Thread() {
				@Override public void run() {
					ImagePreview.showRaster( out, screenW*2, screenH*2 );
				}	
			}.start();
					
			writeOut();
			broot.quality = false;
		}
	}
	
	private void writeOut() {
		BufferedImage i = new BufferedImage( out.getWidth(), out.getHeight(), BufferedImage.TYPE_INT_ARGB );
		
		for( int x = 0; x < i.getWidth(); x++ )
			for( int y = 0; y < i.getHeight(); y++ )
				i.setRGB( x, y, out.colorAt( x, y ) );
		
		try {
			ImageIO.write( i, "png", new File( "./screenshots/renderedImage.png" ) );
		} catch( IOException ex ) {
			System.err.println( ex );
		}
	}
	
}
